package jmine.tec.proxy.cglib;

import java.io.Serializable;
import java.lang.reflect.Method;

import jmine.tec.proxy.chain.ChainElement;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * Implements the invocation chain mechanism through CGLIB proxy
 * 
 * @author takeshi
 * 
 */
public class ChainedMethodInterceptor implements MethodInterceptor, Serializable {

    private final ChainElement[] elements;

    private final Object delegate;

    /**
     * 
     * @param delegate
     *            the delegate object that will be called if any. May be null,
     *            in witch case the original method will be called at the end of
     *            the invocation chain
     * @param elements
     *            the elements
     */
    public ChainedMethodInterceptor(final Object delegate, final ChainElement... elements) {
        super();
        this.delegate = delegate;
        this.elements = elements;
    }

    /**
     * 
     * @param elements
     *            e
     */
    public ChainedMethodInterceptor(final ChainElement... elements) {
        this(null, elements);
    }

    /**
     * @param obj the proxy object
     * @param method the method
     * @param args the arguments
     * @param proxy the method proxy
     * @return object
     * @throws Throwable t
     * @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object,
     *      java.lang.reflect.Method, java.lang.Object[],
     *      net.sf.cglib.proxy.MethodProxy)
     */
    public Object intercept(final Object obj, final Method method, final Object[] args,
            final MethodProxy proxy) throws Throwable {
        final boolean useFast = this.delegate == null;
        CGLIBInvocationChain chain;
        if (useFast) {
            chain = new FastCGLIBInvocationChain(obj, elements, method, obj, args, proxy);
        } else {
            chain = new CGLIBInvocationChain(this.delegate, elements, method, obj, args, proxy);
        }
        return chain.proceed();
    }

}
